package com.remoting.core.registry.builder;

import com.remoting.api.registry.ServiceImportRegistry;
import com.remoting.api.registry.ServiceImportRegistryBuilder;
import com.remoting.api.registry.bean.Endpoint;
import com.remoting.api.registry.bean.HeaderInfo;
import com.remoting.api.registry.bean.HostInfo;
import com.remoting.api.registry.bean.ServiceImportInfo;
import com.remoting.api.spi.registry.ServiceImportRegistryFactory;
import com.remoting.core.exception.ServiceImportRegistryConfigrationException;
import com.remoting.core.registry.DefaultServiceImportRegistry;
import com.remoting.tools.XMLUtil;
import org.apache.http.protocol.HTTP;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基于XML配置的服务导入注册表构建器
 * <p>说明:</p>
 * <li></li>
 * @author DuanYong
 * @since 2017年3月3日上午9:53:24
 */
public class XmlServiceImportRegistryBuilder implements ServiceImportRegistryBuilder {
	/*************************************xml节点定义************************************************/
	/**import节点名称*/
	private static final String IMPORT_ELM = "import";
	/**import->url-param子节点名称*/
	private static final String IMPORT_RUL_PARAM_ELM = "url-param";
	/**import->data-param子节点名称*/
	private static final String IMPORT_RUL_DATA_PARAM_ELM = "data-param";
	/**http-endpoint节点名称*/
	private static final String HTTP_ENDPOINT_ELM = "http-endpoint";
	/**http-endpoint->host子节点名称*/
	private static final String HTTP_ENDPOINT_HOST_ELM = "host";
	/**http-header节点名称*/
	private static final String HTTP_HEADER_ELM = "http-header";
	/**http-header->header节点名称*/
	private static final String HTTP_HEADER_HEADER_ELM = "header";

	/*************************************xml节点属性定义************************************************/
	/**import节点包含属性名称*/
    /**服务ID*/
	private static final String IMPORT_SERVICE_ID_ATTR = "service-id";
	/**远程服务接口名称*/
	private static final String IMPORT_API_ATTR = "api";
	/**请求远程服务接口超时时间*/
	private static final String IMPORT_TIMEOUT_ATTR = "timeout";
	/**请求类型:get/post*/
	private static final String IMPORT_REQUEST_TYPE_ATTR = "request-type";
	/**endpoint 配置引用ID*/
	private static final String IMPORT_ENDPOINT_REF_ATTR = "endpoint-ref";
	/**header 配置引用ID*/
	private static final String IMPORT_HEADER_REF_ATTR = "header-ref";
	
	/**http-endpoint 节点包含属性名称*/
	/**远程服务路径*/
	private static final String HTTP_ENDPOINT_SERVICE_ADDR_ATTR = "service-addr";
	/**远程服务协议类型:http/https*/
	private static final String HTTP_ENDPOINT_TYPE = "type";
	
	/**host 节点包含属性名称*/
	/**远程服务地址：域名/IP*/
	private static final String HOST_ADDRESS_ATTR = "address";
	/**远程服务地址端口*/
	private static final String HOST_PORT_ATTR = "port";
	/**appKey*/
	private static final String HOST_APP_KEY_ATTR = "appKey";
	/**secretKey*/
	private static final String HOST_SECRET_KEY_ATTR = "secretKey";
	/**livemode*/
	private static final String HOST_LIVEMODE_ATTR = "livemode";
	/**scope*/
	private static final String HOST_SCOPE_ATTR = "scope";
	
	/**http-header 节点包含属性名称*/
	private static final String HEADER_CHARSET_ATTR = "charset";
	
	/**公共属性名称*/
	/**id属性*/
	private static final String ID_ATTR = "id";
	/**name属性*/
	private static final String NAME_ATTR = "name";
	
	/**
	 * 配置文件路径
	 */
	private String configPath;

	
	private ServiceImportRegistry importRegistry = ServiceImportRegistryFactory.create();
	

	@SuppressWarnings("unchecked")
	private ServiceImportRegistry doBuild() throws ServiceImportRegistryConfigrationException {
		try {
			ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
			Resource[] resources = resourcePatternResolver.getResources(configPath);
			Assert.notNull(resources, "没有匹配到找到任何映射配置文件! config pattern=" + configPath);
			
			/**
			 * 装载所有配置文件
			 */
			for (int i = 0; i < resources.length; i++) {
				Document doc = initDOM(resources[i].getURL());
				Assert.notNull(doc, "初始化DOM失败! location=" + resources[i]);

				Element rootElm = doc.getDocumentElement();

				//解析请求头信息
				List<Element> herderNodeList = XMLUtil.childNodeList(rootElm, HTTP_HEADER_ELM);
				for (Element headerNode : herderNodeList) {
					parseHeader(headerNode);
				}
				//解析endpoint信息
				List<Element> httpNodeList = XMLUtil.childNodeList(rootElm, HTTP_ENDPOINT_ELM);
				for (Element httpNode : httpNodeList) {
					parseHttpEndpoint(httpNode);
				}
				//解析服务信息
				List<Element> importNodeList = XMLUtil.childNodeList(rootElm, IMPORT_ELM);
				for (Element importNode : importNodeList) {
					parseImport(importNode);
				}
			}
		}
		catch (Exception e) {
			throw new ServiceImportRegistryConfigrationException("构建远程服务注册配置失败.", e);
		}
		
		return this.importRegistry;
	}
	/**
	 * 解析Http请求头配置
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param headerNode
	 * @since 2018年2月1日上午10:32:32
	 */
	private void parseHeader(Element headerNode) {
		String id = XMLUtil.getNodeAttributeValue(headerNode, ID_ATTR);
		String charset = XMLUtil.getNodeAttributeValue(headerNode, HEADER_CHARSET_ATTR);
		Map<String,String> headerMap = new HashMap<>();
		HeaderInfo headerInfo = new HeaderInfo(id, charset,headerMap);
		//解析请求头
		List<Element> headerElmList = XMLUtil.childNodeList(headerNode, HTTP_HEADER_HEADER_ELM);
		if(CollectionUtils.isEmpty(headerElmList)){
			Assert.notNull(null, "Http请求头配置有误，至少包含一个请求头信息. ID=" + id);	
		}
		for (Element headerElm : headerElmList) {
			String key = XMLUtil.getNodeAttributeValue(headerElm, NAME_ATTR);
			if(StringUtils.isEmpty(key)){
				Assert.notNull(null, "Http请求头配置有误，请求头信息name不能为空. ID=" + id);	
			}
			String value = XMLUtil.getTextData(headerElm);
			headerMap.put(key, value);
		}
		this.importRegistry.addHeader(headerInfo);
	}
    /**
     * 解析Endpoint节点
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param httpNode
     * @since 2018年12月27日上午9:58:58
     */
	private void parseHttpEndpoint(Element httpNode) {
		String id = XMLUtil.getNodeAttributeValue(httpNode, ID_ATTR);
		String serviceAddr = XMLUtil.getNodeAttributeValue(httpNode, HTTP_ENDPOINT_SERVICE_ADDR_ATTR);
		String type = XMLUtil.getNodeAttributeValue(httpNode, HTTP_ENDPOINT_TYPE);
		List<HostInfo> hostInfoList = parseHosts(httpNode);
		Endpoint endpoint = new Endpoint(id, serviceAddr,type, hostInfoList);
		
		this.importRegistry.addEndpoint(endpoint);
	}
    /**
     * 解析Host节点
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param parentNode
     * @return
     * @since 2018年12月27日上午9:58:29
     */
	@SuppressWarnings("unchecked")
	private List<HostInfo> parseHosts(Element parentNode) {
		List<HostInfo> hosts = new ArrayList<HostInfo>();
		
		List<Element> hostNodeList = XMLUtil.childNodeList(parentNode, HTTP_ENDPOINT_HOST_ELM);
		for (Element hostNode : hostNodeList) {
			String address = XMLUtil.getNodeAttributeValue(hostNode, HOST_ADDRESS_ATTR);
			String port = XMLUtil.getNodeAttributeValue(hostNode, HOST_PORT_ATTR);
			String appKey = XMLUtil.getNodeAttributeValue(hostNode, HOST_APP_KEY_ATTR);
			String secretKey = XMLUtil.getNodeAttributeValue(hostNode, HOST_SECRET_KEY_ATTR);
			String livemode = XMLUtil.getNodeAttributeValue(hostNode, HOST_LIVEMODE_ATTR);
			String scope = XMLUtil.getNodeAttributeValue(hostNode, HOST_SCOPE_ATTR);
			
			hosts.add(new HostInfo(address, port,appKey,secretKey,livemode,scope));
		}
		
		return hosts;
	}
    /**
     * 解析import节点
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param importNode import节点
     */
	private void parseImport(Element importNode) {
		String serviceId = XMLUtil.getNodeAttributeValue(importNode, IMPORT_SERVICE_ID_ATTR);
		String api = XMLUtil.getNodeAttributeValue(importNode, IMPORT_API_ATTR);
		String timeout = XMLUtil.getNodeAttributeValue(importNode, IMPORT_TIMEOUT_ATTR);
		String requestType = XMLUtil.getNodeAttributeValue(importNode, IMPORT_REQUEST_TYPE_ATTR);
		String endpointId = XMLUtil.getNodeAttributeValue(importNode, IMPORT_ENDPOINT_REF_ATTR);
		String headerId = XMLUtil.getNodeAttributeValue(importNode, IMPORT_HEADER_REF_ATTR);
		
		/**
		 * 如果配置了端点引用，则添加该端点实例并较验
		 */
		Endpoint endpoint = null;
		if (!StringUtils.hasText(endpointId)) {
			Assert.notNull(endpoint, "无法构建服务导入配置, 未指定接入端点配置. 服务ID=" + serviceId);	
		}	
		endpoint = this.importRegistry.getEndpoint(endpointId);
		Assert.notNull(endpoint, "无法构建服务导入配置, 未找到指定的接入端点配置. 服务ID=" + serviceId + "  接入端点ID=" + endpointId);	
		/**
		 * 如果配置了Http头信息引用，则添加，否者添加默认头信息
		 */
		HeaderInfo headerInfo = null;
		if (StringUtils.hasText(headerId)) {
			headerInfo = this.importRegistry.getHeader(headerId);
			Assert.notNull(headerInfo, "无法构建服务导入配置, 未找到指定的Http头信息配置. 服务ID=" + serviceId + "  接入Http头信息ID=" + headerId);	
		}else{
			headerInfo = getDefaultHeaderInfo();
		}
		ServiceImportInfo serviceInfo = new ServiceImportInfo(serviceId,api,timeout,endpoint,headerInfo,requestType);
		parseMappingParams(importNode, serviceInfo);
		parseDataParams(importNode, serviceInfo);
		this.importRegistry.addServiceImportInfo(serviceInfo);
	}
	/**
	 * 获取默认http请求头信息
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @return
	 * @since 2018年2月1日上午10:52:18
	 */
	private HeaderInfo getDefaultHeaderInfo() {
		Map<String,String> headerMap = new HashMap<>();
		headerMap.put(HTTP.CONTENT_TYPE, "application/json");
		return  new HeaderInfo("default_header", "UTF-8",headerMap);
	}
    /**
     * 解析url-param
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param serviceNode
     * @param serviceInfo
     */
	@SuppressWarnings("unchecked")
	private void parseMappingParams(Element serviceNode, ServiceImportInfo serviceInfo) {
		List<Element> mappingParamsElmList = XMLUtil.childNodeList(serviceNode, IMPORT_RUL_PARAM_ELM);
		
		for (Element mappingParamElm : mappingParamsElmList) {
			String name = XMLUtil.getNodeAttributeValue(mappingParamElm, NAME_ATTR);
			String value = XMLUtil.getTextData(mappingParamElm);
			
			serviceInfo.addMappingParam(name, value);
		}
	}
	/**
	 * 解析data-param
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param serviceNode
	 * @param serviceInfo
	 */
	private void parseDataParams(Element serviceNode, ServiceImportInfo serviceInfo) {
		List<Element> mappingParamsElmList = XMLUtil.childNodeList(serviceNode, IMPORT_RUL_DATA_PARAM_ELM);
		
		for (Element mappingParamElm : mappingParamsElmList) {
			String name = XMLUtil.getNodeAttributeValue(mappingParamElm, NAME_ATTR);
			String value = XMLUtil.getTextData(mappingParamElm);
			
			serviceInfo.addDataParam(name, value);
		}
	}

	/**
	 * 
	 * 初始化DOM对象
	 */
	private Document initDOM(URL fileUrl) {
		try {
			return XMLUtil.getDocument(fileUrl.openStream());
		}
		catch (Exception e) {
			throw new ServiceImportRegistryConfigrationException("将XML文件初始化为DOM对象时发生异常! 配置文件：" + fileUrl,e);
		}
	}

	@Override
	public ServiceImportRegistry build(String configPath) {
		this.configPath = configPath;
		return this.doBuild();
	}
}
